use std::borrow::Borrow;

use crate::{
    errors::cons_error,
    mdb::{MClient, ModelEx, MDB},
    models::blacklist::{BlacklistContent, BlacklistModel},
    AppError, AppResult, ArcConfig, Pagination, PaginationQuery,
};
use serde::{Deserialize, Serialize};
use wither::{
    bson::{doc, oid::ObjectId, to_bson, Document},
    Model,
};
#[allow(unused)]
#[derive(Debug, Clone)]
pub struct BlacklistService {
    mdb: MDB,
    m_client: MClient,
    config: ArcConfig,
}

impl BlacklistService {
    pub fn new(config: ArcConfig, mdb: MDB, m_client: MClient) -> Self {
        Self {
            config,
            m_client,
            mdb,
        }
    }
    pub async fn add_to_blacklist<T: Into<AddToBlacklistSvrReq>>(
        &self,
        req: T,
    ) -> AppResult<BlacklistModel> {
        let req: AddToBlacklistSvrReq = req.into();
        let mut res = BlacklistModel::from(req);
        res.save(&*self.mdb, None).await?;
        Ok(res)
    }
    pub async fn remove_from_blacklist<T: Into<ObjectId>>(
        &self,
        req: T,
    ) -> AppResult<BlacklistModel> {
        let blacklist_id: ObjectId = req.into();
        let coll = BlacklistModel::collection(&*self.mdb);
        let res = coll
            .find_one_and_delete(
                doc! {
                    "_id":blacklist_id
                },
                None,
            )
            .await?
            .ok_or_else(|| AppError::not_found(cons_error::MSG_BLACKLIST_IS_NOT_FOUND))?;
        Ok(BlacklistModel::instance_from_document(res)?)
    }
    pub async fn check_in_blacklist<T: Into<CheckInBlackListSvrReq>>(
        &self,
        req: T,
    ) -> AppResult<bool> {
        let req: CheckInBlackListSvrReq = req.into();
        let res = BlacklistModel::find_one(
            &*self.mdb,
            doc! {
                "target":req.target_id,
                "content":to_bson(&req.content)?
            },
            None,
        )
        .await?;
        Ok(res.is_some())
    }
    pub async fn search_blacklist<
        T: Into<BlacklistContent>,
        F: Into<Option<Document>>,
        Q: Borrow<PaginationQuery>,
    >(
        &self,
        req: T,
        filter: F,
        query: Q,
    ) -> AppResult<Pagination<BlacklistModel>> {
        let mut filter: Document = filter.into().unwrap_or_default();
        let content: BlacklistContent = req.into();
        filter.insert("content", to_bson(&content)?);

        let res =
            BlacklistModel::find_and_pagination(self.mdb.clone(), filter, query.borrow()).await?;

        Ok(res)
    }

    pub async fn find_one_blacklist<T: Into<Option<ObjectId>>, F: Into<Option<Document>>>(
        &self,
        blacklist_id: T,
        filter: F,
    ) -> AppResult<Option<BlacklistModel>> {
        let mut filter: Document = filter.into().unwrap_or_default();
        let blacklist_id: Option<ObjectId> = blacklist_id.into();
        if let Some(blacklist_id) = blacklist_id {
            filter.insert("_id", blacklist_id);
        }
        Ok(BlacklistModel::find_one(&*self.mdb, filter, None).await?)
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AddToBlacklistSvrReq {
    pub target_id: ObjectId,
    pub creator_id: ObjectId,
    pub content: BlacklistContent,
}

impl From<AddToBlacklistSvrReq> for BlacklistModel {
    fn from(req: AddToBlacklistSvrReq) -> Self {
        Self {
            target: req.target_id,
            creator: req.creator_id,
            content: req.content,
            ..Default::default()
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CheckInBlackListSvrReq {
    pub target_id: ObjectId,
    pub content: BlacklistContent,
}
